import easyquotation
from typing import Dict, List, Optional, Tuple
import yaml
import time
import os
import logging
from pathlib import Path
import requests
from utils.price_formatter import format_price, format_change_percent


class AStockRanking:
    """A股涨幅排名模块"""
    
    def __init__(self, data_source: str = 'tencent'):
        """初始化A股涨幅排名模块
        
        Args:
            data_source: 数据源 ('sina', 'tencent', 'yfinance', 'auto')
        """
        self.data_source = data_source
        self.quotation = None
        self.available_sources = ['sina', 'tencent', 'yfinance']  # 按稳定性排序
        self.is_auto_mode = data_source == 'auto'
        self._setup_logging()
        self._setup_quotation_service()
    
    def _setup_logging(self):
        """设置日志配置"""
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(levelname)s - %(message)s',
            handlers=[
                logging.StreamHandler(),
                logging.FileHandler('stock_ranking.log')
            ]
        )
        self.logger = logging.getLogger(__name__)
    
    def _setup_quotation_service(self):
        """设置行情服务"""
        try:
            # Normalize source name
            source = self.data_source.lower().strip()
            
            if source == 'auto':
                # auto模式使用第一个可用数据源
                source = self.available_sources[0]
                self.logger.info(f"Auto mode enabled, using primary source: {source}")
            
            if source in ['sina', '新浪']:
                self.quotation = easyquotation.use('sina')
            elif source in ['tencent', 'qq', '腾讯']:
                self.quotation = easyquotation.use('tencent')
            elif source in ['yfinance', 'yahoo', 'yf']:
                # 导入yfinance适配器
                from .yfinance_source import YFinanceQuotation
                self.quotation = YFinanceQuotation()
            else:
                self.logger.warning(f"Unknown data source '{source}', using default 'tencent'")
                self.quotation = easyquotation.use('tencent')
            
            self.logger.info(f"Ranking module data source: {self.data_source}")
                
        except Exception as e:
            self.logger.error(f"Failed to create quotation service: {e}")
            raise
    
    def get_all_stocks_basic_info(self) -> List[str]:
        """获取所有A股代码列表
        
        覆盖A股市场所有代码范围，包括：
        - 沪市主板 (600000-603999)
        - 沪市科创板 (688000-688999) 
        - 深市主板 (000001-000999)
        - 深市中小板 (002000-002999)
        - 创业板 (300000-300999)
        """
        stock_codes = []
        
        self.logger.info("开始生成A股市场所有股票代码...")
        
        # 沪市主板 (600000-603999) - 约4000只
        self.logger.info("添加沪市主板股票代码...")
        for i in range(600000, 604000):
            stock_codes.append(f"{i:06d}")
        
        # 沪市科创板 (688000-688999) - 约1000只
        self.logger.info("添加科创板股票代码...")
        for i in range(688000, 689000):
            stock_codes.append(f"{i:06d}")
        
        # 深市主板 (000001-000999) - 约1000只
        self.logger.info("添加深市主板股票代码...")
        for i in range(1, 1000):
            stock_codes.append(f"{i:06d}")
        
        # 深市中小板 (002000-002999) - 约1000只
        self.logger.info("添加中小板股票代码...")
        for i in range(2000, 3000):
            stock_codes.append(f"{i:06d}")
        
        # 创业板 (300000-300999) - 约1000只
        self.logger.info("添加创业板股票代码...")
        for i in range(300000, 301000):
            stock_codes.append(f"{i:06d}")
        
        self.logger.info(f"生成完成，共 {len(stock_codes)} 个A股代码")
        return stock_codes
    
    def get_all_stocks_data(self) -> Dict:
        """获取全市场股票数据
        
        Returns:
            Dict: 股票行情数据
        """
        try:
            self.logger.info("开始获取全市场股票数据...")
            
            # 获取所有股票代码
            stock_codes = self.get_all_stocks_basic_info()
            
            # 批量获取数据
            quotes_data, stats = self.get_stocks_data_batch(stock_codes)
            
            if quotes_data:
                self.logger.info(f"成功获取 {len(quotes_data)} 只股票的行情数据")
                return quotes_data
            else:
                self.logger.warning("未能获取到任何股票数据")
                return {}
                
        except Exception as e:
            self.logger.error(f"获取全市场数据失败: {e}")
            return {}
    
    def get_stocks_data_batch(self, stock_codes: List[str]) -> tuple:
        """批量获取股票数据（带重试机制和统计信息）
        
        Args:
            stock_codes: 股票代码列表
            
        Returns:
            tuple: (股票数据字典, 统计信息字典)
        """
        try:
            if not self.quotation:
                self.logger.error("Quotation service not initialized")
                return {}, {}
            
            # 统计信息
            stats = {
                'total_planned': len(stock_codes),
                'total_success': 0,
                'total_failed': 0,
                'failed_by_sector': {
                    '沪市主板': 0,
                    '科创板': 0,
                    '深市主板': 0,
                    '中小板': 0,
                    '创业板': 0
                },
                'failed_batches': []
            }
            
            # 分批获取数据，避免请求过大
            batch_size = 50  # 降低批次大小提高成功率
            all_quotes = {}
            total_batches = (len(stock_codes) + batch_size - 1) // batch_size
            failed_batches = []
            
            print(f"🔄 开始批量获取，共{total_batches}批，每批{batch_size}只股票")
            
            # 第一轮获取
            for i in range(0, len(stock_codes), batch_size):
                batch_codes = stock_codes[i:i+batch_size]
                batch_num = i // batch_size + 1
                
                success = self._get_single_batch(batch_codes, batch_num, all_quotes)
                if not success:
                    failed_batches.append((batch_codes, batch_num))
                    # 统计失败的批次按板块分类
                    for code in batch_codes:
                        sector = self._get_stock_sector(code)
                        stats['failed_by_sector'][sector] += 1
                
                # 适度延迟，避免请求过快
                time.sleep(0.1)
            
            # 重试失败的批次
            if failed_batches:
                print(f"🔄 重试{len(failed_batches)}个失败批次...")
                retry_failed = []
                for batch_codes, batch_num in failed_batches:
                    print(f"🔄 重试第{batch_num}批...")
                    success = self._get_single_batch(batch_codes, batch_num, all_quotes)
                    if success:
                        print(f"✅ 第{batch_num}批重试成功")
                        # 重试成功的从失败统计中移除
                        for code in batch_codes:
                            sector = self._get_stock_sector(code)
                            stats['failed_by_sector'][sector] -= 1
                    else:
                        print(f"❌ 第{batch_num}批重试仍失败")
                        retry_failed.append(batch_num)
                    time.sleep(0.2)  # 重试时延迟更长
                
                stats['failed_batches'] = retry_failed
            
            # 统计最终结果
            stats['total_success'] = len(all_quotes)
            stats['total_failed'] = stats['total_planned'] - stats['total_success']
            
            # 显示统计信息
            self._display_batch_stats(stats)
            
            return all_quotes, stats
            
        except Exception as e:
            self.logger.error(f"Failed to get stocks data: {e}")
            return {}, {}
    
    def _get_single_batch(self, batch_codes: List[str], batch_num: int, all_quotes: Dict) -> bool:
        """获取单个批次的数据
        
        Args:
            batch_codes: 批次股票代码
            batch_num: 批次号
            all_quotes: 总数据字典
            
        Returns:
            bool: 是否成功
        """
        try:
            print(f"📥 第{batch_num}批: 请求股票代码 {batch_codes[0]} ~ {batch_codes[-1]} (共{len(batch_codes)}只)")
            
            if self.is_auto_mode:
                # auto模式：单个股票级别智能切换
                return self._get_batch_with_auto_switch(batch_codes, batch_num, all_quotes)
            else:
                # 传统模式：批量获取
                return self._get_batch_traditional(batch_codes, batch_num, all_quotes)
                
        except Exception as e:
            self.logger.error(f"第{batch_num}批获取失败: {e}")
            print(f"❌ 第{batch_num}批获取失败: {e}")
            return False
    
    def _get_batch_traditional(self, batch_codes: List[str], batch_num: int, all_quotes: Dict) -> bool:
        """传统批量获取模式"""
        try:
            batch_quotes = self.quotation.stocks(batch_codes)
            if batch_quotes and len(batch_quotes) > 0:
                all_quotes.update(batch_quotes)
                
                # 只显示前5只股票，避免输出过多
                print(f"✅ 第{batch_num}批成功获取 {len(batch_quotes)} 只股票:")
                count = 0
                for code, data in batch_quotes.items():
                    if count >= 5:  # 只显示前5只
                        break
                    try:
                        name = data.get('name', '未知')
                        price = data.get('now', '0')
                        if price == '0' or price == 0:
                            print(f"   ⚠️  {code} {name} 现价:{price} (停牌/无数据)")
                        else:
                            print(f"   📈 {code} {name} 现价:{price}")
                        count += 1
                    except Exception as e:
                        print(f"   ❌ {code} 数据解析错误: {str(e)}")
                        count += 1
                
                if len(batch_quotes) > 5:
                    print(f"   ... 还有 {len(batch_quotes) - 5} 只股票")
                
                return True
            else:
                print(f"❌ 第{batch_num}批未获取到数据")
                return False
        except Exception as e:
            print(f"❌ 第{batch_num}批获取失败: {e}")
            return False
    
    def _get_batch_with_auto_switch(self, batch_codes: List[str], batch_num: int, all_quotes: Dict) -> bool:
        """auto模式：单个股票级别智能切换"""
        try:
            batch_success_count = 0
            batch_quotes = {}
            
            # 为批次中的每只股票尝试获取数据
            for code in batch_codes:
                stock_data = self._get_single_stock_with_auto_switch(code)
                if stock_data:
                    batch_quotes.update(stock_data)
                    batch_success_count += 1
            
            # 更新总数据
            if batch_quotes:
                all_quotes.update(batch_quotes)
                
                # 显示批次结果
                print(f"🔄 第{batch_num}批auto模式: 成功获取 {batch_success_count}/{len(batch_codes)} 只股票")
                
                # 显示前5只股票
                count = 0
                for code, data in batch_quotes.items():
                    if count >= 5:
                        break
                    try:
                        name = data.get('name', '未知')
                        price = data.get('now', '0')
                        if price == '0' or price == 0:
                            print(f"   ⚠️  {code} {name} 现价:{price} (停牌/无数据)")
                        else:
                            print(f"   📈 {code} {name} 现价:{price}")
                        count += 1
                    except Exception as e:
                        print(f"   ❌ {code} 数据解析错误: {str(e)}")
                        count += 1
                
                if len(batch_quotes) > 5:
                    print(f"   ... 还有 {len(batch_quotes) - 5} 只股票")
                
                return batch_success_count > 0
            else:
                print(f"❌ 第{batch_num}批auto模式: 未获取到任何数据")
                return False
                
        except Exception as e:
            print(f"❌ 第{batch_num}批auto模式失败: {e}")
            return False
    
    def _get_single_stock_with_auto_switch(self, code: str) -> Optional[Dict]:
        """为单个股票尝试多个数据源获取数据"""
        for source_name in self.available_sources:
            try:
                # 对于yfinance，进行快速失败检测
                if source_name == 'yfinance':
                    # 跳过yfinance，因为目前遇到严重速率限制
                    continue
                
                # 创建临时数据源
                temp_quotation = self._create_temp_quotation(source_name)
                if temp_quotation:
                    # 尝试获取单个股票数据
                    stock_data = temp_quotation.stocks([code])
                    if stock_data and code in stock_data:
                        # 验证数据有效性
                        data = stock_data[code]
                        if self._is_valid_stock_data(data):
                            return stock_data
            except Exception as e:
                # 当前数据源失败，尝试下一个
                continue
        
        # 所有数据源都失败
        return None
    
    def _create_temp_quotation(self, source_name: str):
        """创建临时数据源"""
        try:
            if source_name in ['sina', '新浪']:
                import easyquotation
                return easyquotation.use('sina')
            elif source_name in ['tencent', 'qq', '腾讯']:
                import easyquotation
                return easyquotation.use('tencent')
            elif source_name in ['yfinance', 'yahoo', 'yf']:
                from .yfinance_source import YFinanceQuotation
                return YFinanceQuotation()
            else:
                return None
        except Exception as e:
            return None
    
    def _is_valid_stock_data(self, data: Dict) -> bool:
        """验证股票数据是否有效"""
        try:
            # 检查必要字段
            required_fields = ['name', 'now', 'close']
            for field in required_fields:
                if field not in data:
                    return False
            
            # 检查价格数据
            current_price = float(data['now'])
            if current_price <= 0:
                return False
            
            return True
        except (ValueError, TypeError, KeyError):
            return False
    
    def _get_stock_sector(self, code: str) -> str:
        """根据股票代码判断所属板块"""
        if code.startswith('60'):
            return '沪市主板'
        elif code.startswith('688'):
            return '科创板'
        elif code.startswith('000'):
            return '深市主板'
        elif code.startswith('002'):
            return '中小板'
        elif code.startswith('300'):
            return '创业板'
        else:
            return '其他'
    
    def _display_batch_stats(self, stats: Dict):
        """显示批量获取统计信息"""
        print(f"\n📊 数据获取统计报告")
        print(f"{'='*60}")
        print(f"📋 计划获取: {stats['total_planned']} 只股票")
        print(f"✅ 成功获取: {stats['total_success']} 只股票")
        print(f"❌ 获取失败: {stats['total_failed']} 只股票")
        print(f"📈 成功率: {stats['total_success']/stats['total_planned']*100:.1f}%")
        
        if stats['total_failed'] > 0:
            print(f"\n🗂  失败股票分布:")
            for sector, count in stats['failed_by_sector'].items():
                if count > 0:
                    print(f"   {sector}: {count} 只")
        
        if stats.get('failed_batches'):
            print(f"\n⚠️  失败批次: {len(stats['failed_batches'])} 个")
        
        print(f"{'='*60}")
    
    def calculate_ranking_by_change_percent(self, quotes_data: Dict) -> List[Tuple[str, Dict, float]]:
        """计算涨幅排名
        
        Args:
            quotes_data: 股票行情数据
            
        Returns:
            List[Tuple[str, Dict, float]]: 排序后的股票列表 (代码, 数据, 涨幅百分比)
        """
        ranking_data = []
        
        for code, data in quotes_data.items():
            try:
                current_price = float(data['now'])
                yesterday_close = float(data['close'])
                
                # 跳过停牌或异常数据
                if current_price <= 0 or yesterday_close <= 0:
                    continue
                
                # 计算涨幅百分比
                change_percent = ((current_price - yesterday_close) / yesterday_close) * 100
                
                # 过滤掉过大的涨幅（可能是数据错误）
                # A股有涨跌停限制，一般为10%，ST股为5%，科创板和创业板为20%
                if abs(change_percent) > 25:  # 涨跌超过25%的可能有问题
                    continue
                
                ranking_data.append((code, data, change_percent))
                
            except (KeyError, ValueError, TypeError) as e:
                # 跳过无效数据
                continue
        
        # 按涨幅降序排序
        ranking_data.sort(key=lambda x: x[2], reverse=True)
        
        return ranking_data
    
    def get_top_gainers(self, top_n: int = 50) -> List[Tuple[str, Dict, float]]:
        """获取涨幅排名前N的股票
        
        Args:
            top_n: 获取前N名，默认50
            
        Returns:
            List[Tuple[str, Dict, float]]: 前N名股票列表
        """
        try:
            # 先测试连接
            if not self.test_connection():
                print("❌ 数据源连接失败，请检查网络连接或稍后重试")
                return []
            
            # 获取股票代码列表
            stock_codes = self.get_all_stocks_basic_info()
            print(f"🔍 已生成 {len(stock_codes)} 个A股代码，开始获取实时数据...")
            
            # 批量获取股票数据
            quotes_data, stats = self.get_stocks_data_batch(stock_codes)
            
            if not quotes_data:
                self.logger.error("No stock data retrieved")
                print("❌ 未能获取到任何股票数据，请检查网络连接或稍后重试")
                return []
            
            # 计算涨幅排名
            ranking_data = self.calculate_ranking_by_change_percent(quotes_data)
            print(f"📊 计算完成，共 {len(ranking_data)} 只股票有效涨幅数据")
            
            if not ranking_data:
                print("❌ 未能计算出有效的涨幅排名数据")
                return []
            
            # 返回前N名
            top_gainers = ranking_data[:top_n]
            
            return top_gainers
            
        except Exception as e:
            self.logger.error(f"Failed to get top gainers: {e}")
            print(f"❌ 获取涨幅排名失败: {e}")
            return []
    
    def get_top_losers(self, top_n: int = 50) -> List[Tuple[str, Dict, float]]:
        """获取跌幅排名前N的股票
        
        Args:
            top_n: 获取前N名，默认50
            
        Returns:
            List[Tuple[str, Dict, float]]: 前N名股票列表（按跌幅从大到小）
        """
        try:
            # 先测试连接
            if not self.test_connection():
                print("❌ 数据源连接失败，请检查网络连接或稍后重试")
                return []
            
            # 获取股票代码列表
            stock_codes = self.get_all_stocks_basic_info()
            print(f"🔍 已生成 {len(stock_codes)} 个A股代码，开始获取实时数据...")
            
            # 批量获取股票数据
            quotes_data, stats = self.get_stocks_data_batch(stock_codes)
            
            if not quotes_data:
                self.logger.error("No stock data retrieved")
                print("❌ 未能获取到任何股票数据，请检查网络连接或稍后重试")
                return []
            
            # 计算涨幅排名
            ranking_data = self.calculate_ranking_by_change_percent(quotes_data)
            print(f"📊 计算完成，共 {len(ranking_data)} 只股票有效涨幅数据")
            
            if not ranking_data:
                print("❌ 未能计算出有效的涨幅排名数据")
                return []
            
            # 反向排序获取跌幅最大的股票
            ranking_data.sort(key=lambda x: x[2])  # 按涨幅升序排序，负数最大的在前
            
            # 返回前N名跌幅最大的
            top_losers = ranking_data[:top_n]
            
            return top_losers
            
        except Exception as e:
            self.logger.error(f"Failed to get top losers: {e}")
            print(f"❌ 获取跌幅排名失败: {e}")
            return []
    
    def get_limit_up_stocks(self, exclude_st: bool = True) -> List[Tuple[str, Dict, float]]:
        """获取涨停股票列表
        
        Args:
            exclude_st: 是否排除ST股票，默认True
            
        Returns:
            List[Tuple[str, Dict, float]]: 涨停股票列表
        """
        try:
            # 先测试连接
            if not self.test_connection():
                print("❌ 数据源连接失败，请检查网络连接或稍后重试")
                return []
            
            # 获取股票代码列表
            stock_codes = self.get_all_stocks_basic_info()
            print(f"🔍 已生成 {len(stock_codes)} 个A股代码，开始获取实时数据...")
            
            # 批量获取股票数据
            quotes_data, stats = self.get_stocks_data_batch(stock_codes)
            
            if not quotes_data:
                self.logger.error("No stock data retrieved")
                print("❌ 未能获取到任何股票数据，请检查网络连接或稍后重试")
                return []
            
            # 筛选涨停股票
            limit_up_stocks = []
            
            for code, data in quotes_data.items():
                try:
                    current_price = float(data['now'])
                    yesterday_close = float(data['close'])
                    
                    # 跳过停牌或异常数据
                    if current_price <= 0 or yesterday_close <= 0:
                        continue
                    
                    # 计算涨幅百分比
                    change_percent = ((current_price - yesterday_close) / yesterday_close) * 100
                    
                    # 判断是否为ST股票
                    stock_name = data.get('name', '')
                    is_st_stock = any(prefix in stock_name for prefix in ['ST', '*ST', 'S*ST', 'SST'])
                    
                    # 如果排除ST股票且当前是ST股票，则跳过
                    if exclude_st and is_st_stock:
                        continue
                    
                    # 判断涨停条件
                    is_limit_up = False
                    
                    # 科创板和创业板（300、688开头）：20%涨停
                    if code.startswith(('300', '688')):
                        if change_percent >= 19.5:  # 考虑数据误差，使用19.5%作为阈值
                            is_limit_up = True
                    # ST股票：5%涨停
                    elif is_st_stock:
                        if change_percent >= 4.5:  # 考虑数据误差，使用4.5%作为阈值
                            is_limit_up = True
                    # 主板股票：10%涨停
                    else:
                        if change_percent >= 9.5:  # 考虑数据误差，使用9.5%作为阈值
                            is_limit_up = True
                    
                    if is_limit_up:
                        limit_up_stocks.append((code, data, change_percent))
                        
                except (KeyError, ValueError, TypeError) as e:
                    # 跳过无效数据
                    continue
            
            # 按涨幅降序排序
            limit_up_stocks.sort(key=lambda x: x[2], reverse=True)
            
            print(f"🚀 找到 {len(limit_up_stocks)} 只涨停股票")
            
            return limit_up_stocks
            
        except Exception as e:
            self.logger.error(f"Failed to get limit up stocks: {e}")
            return []
    
    def display_limit_up_ranking(self, limit_up_data: List[Tuple[str, Dict, float]]):
        """显示涨停股票排名
        
        Args:
            limit_up_data: 涨停股票数据
        """
        if not limit_up_data:
            print("❌ 没有找到涨停股票")
            return
        
        current_time = time.strftime('%Y-%m-%d %H:%M:%S')
        
        # 定义各列的宽度（适配终端显示）
        col_widths = {
            'rank': 4,
            'name': 14,  # 涨停榜名称列需要更宽，因为有股票类型标识
            'code': 8,
            'price': 10,
            'change': 12,
            'volume': 10,
            'high': 8,
            'low': 8
        }
        
        total_width = sum(col_widths.values()) + 7  # 7个分隔符
        
        print(f"\n{'='*total_width}")
        print(f"🚀 A股涨停榜 - 共{len(limit_up_data)}只 - {current_time}")
        print(f"{'='*total_width}")
        
        # 打印表头（与数据行对齐方式一致）
        header = (f"{self._pad_string('序号', col_widths['rank'])} "
                 f"{self._pad_string('股票名称', col_widths['name'])} "
                 f"{self._pad_string('代码', col_widths['code'])} "
                 f"{self._pad_string('现价', col_widths['price'], 'right')} "
                 f"{self._pad_string('涨幅', col_widths['change'], 'right')} "
                 f"{self._pad_string('成交量', col_widths['volume'], 'right')} "
                 f"{self._pad_string('最高', col_widths['high'], 'right')} "
                 f"{self._pad_string('最低', col_widths['low'], 'right')}")
        print(header)
        print("-" * total_width)
        
        for rank, (code, data, change_percent) in enumerate(limit_up_data, 1):
            try:
                current_price = float(data['now'])
                yesterday_close = float(data['close'])
                change_amount = current_price - yesterday_close
                
                # 格式化价格显示（ETF显示3位小数，普通股票2位小数）
                stock_name = data['name']
                price_str = format_price(current_price, stock_code=code, stock_name=stock_name)
                high_str = format_price(data['high'], stock_code=code, stock_name=stock_name)
                low_str = format_price(data['low'], stock_code=code, stock_name=stock_name)
                
                # 格式化成交量
                volume = int(data['turnover'])
                volume_str = self._format_volume(volume)
                
                # 格式化涨幅显示（带颜色，智能小数位数）
                change_percent_str = format_change_percent(change_percent, stock_code=code, stock_name=stock_name)
                # 涨停股票通常都是上涨的，所以显示红色
                colored_change = f"\033[31m{change_percent_str}\033[0m"  # 红色
                
                # 判断股票类型，添加标识
                if any(prefix in stock_name for prefix in ['ST', '*ST', 'S*ST', 'SST']):
                    stock_type = "ST"
                elif code.startswith('688'):
                    stock_type = "科创"
                elif code.startswith('300'):
                    stock_type = "创业"
                else:
                    stock_type = "主板"
                
                display_name = f"{stock_name}({stock_type})"
                
                # 打印数据行
                row = (f"{self._pad_string(str(rank), col_widths['rank'])} "
                      f"{self._pad_string(display_name, col_widths['name'])} "
                      f"{self._pad_string(code, col_widths['code'])} "
                      f"{self._pad_string(price_str, col_widths['price'], 'right')} "
                      f"{self._pad_string(colored_change, col_widths['change'], 'right')} "
                      f"{self._pad_string(volume_str, col_widths['volume'], 'right')} "
                      f"{self._pad_string(high_str, col_widths['high'], 'right')} "
                      f"{self._pad_string(low_str, col_widths['low'], 'right')}")
                print(row)
                
            except (KeyError, ValueError, TypeError) as e:
                error_row = (f"{self._pad_string(str(rank), col_widths['rank'])} "
                           f"{self._pad_string('数据错误', col_widths['name'])} "
                           f"{self._pad_string(code, col_widths['code'])} "
                           f"{self._pad_string('--', col_widths['price'], 'right')} "
                           f"{self._pad_string('--', col_widths['change'], 'right')} "
                           f"{self._pad_string('--', col_widths['volume'], 'right')} "
                           f"{self._pad_string('--', col_widths['high'], 'right')} "
                           f"{self._pad_string('--', col_widths['low'], 'right')}")
                print(error_row)
        
        print("-" * total_width)
        print(f"📊 共 {len(limit_up_data)} 只涨停股票 | 数据源: {self.data_source}")
        print("💡 提示：科创板/创业板涨停20%，主板涨停10%，ST股涨停5%")
        print("颜色说明: \033[31m红色=涨停\033[0m")
    
    def _format_volume(self, volume: int) -> str:
        """格式化成交量显示"""
        if volume >= 100000000:  # 100M+
            return f"{volume/100000000:.1f}B"
        elif volume >= 1000000:  # 1M+
            return f"{volume/1000000:.1f}M"
        elif volume >= 1000:     # 1K+
            return f"{volume/1000:.1f}K"
        else:
            return str(volume)
    
    def _get_display_width(self, text: str) -> int:
        """计算字符串的显示宽度（考虑中文字符占用2个字符宽度，忽略ANSI颜色代码）"""
        import re
        # 移除ANSI颜色代码
        clean_text = re.sub(r'\033\[[0-9;]*m', '', text)
        
        width = 0
        for char in clean_text:
            if ord(char) > 127:  # 非ASCII字符（包括中文）
                width += 2
            else:
                width += 1
        return width
    
    def _pad_string(self, text: str, target_width: int, align: str = 'left') -> str:
        """根据显示宽度填充字符串到指定宽度"""
        import re
        # 移除ANSI颜色代码来计算实际显示宽度
        clean_text = re.sub(r'\033\[[0-9;]*m', '', text)
        current_width = self._get_display_width(clean_text)
        
        if current_width >= target_width:
            return text
        
        padding = target_width - current_width
        if align == 'left':
            return text + ' ' * padding
        elif align == 'right':
            return ' ' * padding + text
        else:  # center
            left_padding = padding // 2
            right_padding = padding - left_padding
            return ' ' * left_padding + text + ' ' * right_padding
    
    def display_ranking(self, ranking_data: List[Tuple[str, Dict, float]], top_n: int = 50):
        """显示涨幅排名
        
        Args:
            ranking_data: 排名数据
            top_n: 显示前N名
        """
        if not ranking_data:
            print("❌ 没有获取到排名数据")
            return
        
        current_time = time.strftime('%Y-%m-%d %H:%M:%S')
        
        # 定义各列的宽度（适配终端显示）
        col_widths = {
            'rank': 4,
            'name': 10,
            'code': 8,
            'price': 10,
            'change': 12,
            'volume': 10,
            'high': 8,
            'low': 8
        }
        
        total_width = sum(col_widths.values()) + 7  # 7个分隔符
        
        print(f"\n{'='*total_width}")
        print(f"📈 A股涨幅排行榜 TOP{min(top_n, len(ranking_data))} - {current_time}")
        print(f"{'='*total_width}")
        
        # 打印表头（与数据行对齐方式一致）
        header = (f"{self._pad_string('排名', col_widths['rank'])} "
                 f"{self._pad_string('股票名称', col_widths['name'])} "
                 f"{self._pad_string('代码', col_widths['code'])} "
                 f"{self._pad_string('现价', col_widths['price'], 'right')} "
                 f"{self._pad_string('涨幅', col_widths['change'], 'right')} "
                 f"{self._pad_string('成交量', col_widths['volume'], 'right')} "
                 f"{self._pad_string('最高', col_widths['high'], 'right')} "
                 f"{self._pad_string('最低', col_widths['low'], 'right')}")
        print(header)
        print("-" * total_width)
        
        for rank, (code, data, change_percent) in enumerate(ranking_data[:top_n], 1):
            try:
                current_price = float(data['now'])
                yesterday_close = float(data['close'])
                change_amount = current_price - yesterday_close
                
                # 格式化价格显示（ETF显示3位小数，普通股票2位小数）
                stock_name = data.get('name', '')
                price_str = format_price(current_price, stock_code=code, stock_name=stock_name)
                high_str = format_price(data['high'], stock_code=code, stock_name=stock_name)
                low_str = format_price(data['low'], stock_code=code, stock_name=stock_name)
                
                # 格式化成交量
                volume = int(data['turnover'])
                volume_str = self._format_volume(volume)
                
                # 格式化涨幅显示（带颜色，智能小数位数）
                change_percent_str = format_change_percent(change_percent, stock_code=code, stock_name=stock_name)
                if change_percent > 0:
                    colored_change = f"\033[31m{change_percent_str}\033[0m"  # 红色
                elif change_percent < 0:
                    colored_change = f"\033[32m{change_percent_str}\033[0m"  # 绿色
                else:
                    colored_change = change_percent_str  # 无变化，无颜色
                
                # 打印数据行
                row = (f"{self._pad_string(str(rank), col_widths['rank'])} "
                      f"{self._pad_string(data['name'], col_widths['name'])} "
                      f"{self._pad_string(code, col_widths['code'])} "
                      f"{self._pad_string(price_str, col_widths['price'], 'right')} "
                      f"{self._pad_string(colored_change, col_widths['change'], 'right')} "
                      f"{self._pad_string(volume_str, col_widths['volume'], 'right')} "
                      f"{self._pad_string(high_str, col_widths['high'], 'right')} "
                      f"{self._pad_string(low_str, col_widths['low'], 'right')}")
                print(row)
                
            except (KeyError, ValueError, TypeError) as e:
                error_row = (f"{self._pad_string(str(rank), col_widths['rank'])} "
                           f"{self._pad_string('数据错误', col_widths['name'])} "
                           f"{self._pad_string(code, col_widths['code'])} "
                           f"{self._pad_string('--', col_widths['price'], 'right')} "
                           f"{self._pad_string('--', col_widths['change'], 'right')} "
                           f"{self._pad_string('--', col_widths['volume'], 'right')} "
                           f"{self._pad_string('--', col_widths['high'], 'right')} "
                           f"{self._pad_string('--', col_widths['low'], 'right')}")
                print(error_row)
        
        print("-" * total_width)
        print(f"📊 共显示 {min(top_n, len(ranking_data))} 只股票 | 数据源: {self.data_source}")
        print("颜色说明: \033[31m红色=上涨\033[0m, \033[32m绿色=下跌\033[0m")
    
    def display_losers_ranking(self, ranking_data: List[Tuple[str, Dict, float]], top_n: int = 50):
        """显示跌幅排名
        
        Args:
            ranking_data: 排名数据
            top_n: 显示前N名
        """
        if not ranking_data:
            print("❌ 没有获取到排名数据")
            return
        
        current_time = time.strftime('%Y-%m-%d %H:%M:%S')
        
        # 定义各列的宽度（适配终端显示）
        col_widths = {
            'rank': 4,
            'name': 10,
            'code': 8,
            'price': 10,
            'change': 12,
            'volume': 10,
            'high': 8,
            'low': 8
        }
        
        total_width = sum(col_widths.values()) + 7  # 7个分隔符
        
        print(f"\n{'='*total_width}")
        print(f"📉 A股跌幅排行榜 TOP{min(top_n, len(ranking_data))} - {current_time}")
        print(f"{'='*total_width}")
        
        # 打印表头（与数据行对齐方式一致）
        header = (f"{self._pad_string('排名', col_widths['rank'])} "
                 f"{self._pad_string('股票名称', col_widths['name'])} "
                 f"{self._pad_string('代码', col_widths['code'])} "
                 f"{self._pad_string('现价', col_widths['price'], 'right')} "
                 f"{self._pad_string('跌幅', col_widths['change'], 'right')} "
                 f"{self._pad_string('成交量', col_widths['volume'], 'right')} "
                 f"{self._pad_string('最高', col_widths['high'], 'right')} "
                 f"{self._pad_string('最低', col_widths['low'], 'right')}")
        print(header)
        print("-" * total_width)
        
        for rank, (code, data, change_percent) in enumerate(ranking_data[:top_n], 1):
            try:
                current_price = float(data['now'])
                yesterday_close = float(data['close'])
                change_amount = current_price - yesterday_close
                
                # 格式化价格显示（ETF显示3位小数，普通股票2位小数）
                stock_name = data.get('name', '')
                price_str = format_price(current_price, stock_code=code, stock_name=stock_name)
                high_str = format_price(data['high'], stock_code=code, stock_name=stock_name)
                low_str = format_price(data['low'], stock_code=code, stock_name=stock_name)
                
                # 格式化成交量
                volume = int(data['turnover'])
                volume_str = self._format_volume(volume)
                
                # 格式化跌幅显示（带颜色，智能小数位数）
                change_percent_str = format_change_percent(change_percent, stock_code=code, stock_name=stock_name)
                if change_percent > 0:
                    colored_change = f"\033[31m{change_percent_str}\033[0m"  # 红色
                elif change_percent < 0:
                    colored_change = f"\033[32m{change_percent_str}\033[0m"  # 绿色
                else:
                    colored_change = change_percent_str  # 无变化，无颜色
                
                # 打印数据行
                row = (f"{self._pad_string(str(rank), col_widths['rank'])} "
                      f"{self._pad_string(data['name'], col_widths['name'])} "
                      f"{self._pad_string(code, col_widths['code'])} "
                      f"{self._pad_string(price_str, col_widths['price'], 'right')} "
                      f"{self._pad_string(colored_change, col_widths['change'], 'right')} "
                      f"{self._pad_string(volume_str, col_widths['volume'], 'right')} "
                      f"{self._pad_string(high_str, col_widths['high'], 'right')} "
                      f"{self._pad_string(low_str, col_widths['low'], 'right')}")
                print(row)
                
            except (KeyError, ValueError, TypeError) as e:
                error_row = (f"{self._pad_string(str(rank), col_widths['rank'])} "
                           f"{self._pad_string('数据错误', col_widths['name'])} "
                           f"{self._pad_string(code, col_widths['code'])} "
                           f"{self._pad_string('--', col_widths['price'], 'right')} "
                           f"{self._pad_string('--', col_widths['change'], 'right')} "
                           f"{self._pad_string('--', col_widths['volume'], 'right')} "
                           f"{self._pad_string('--', col_widths['high'], 'right')} "
                           f"{self._pad_string('--', col_widths['low'], 'right')}")
                print(error_row)
        
        print("-" * total_width)
        print(f"📊 共显示 {min(top_n, len(ranking_data))} 只股票 | 数据源: {self.data_source}")
        print("颜色说明: \033[31m红色=上涨\033[0m, \033[32m绿色=下跌\033[0m")
    
    def test_connection(self) -> bool:
        """测试数据源连接"""
        try:
            if not self.quotation:
                self.logger.error("Quotation service not initialized")
                return False
            
            print("🔍 测试数据源连接...")
            
            # 测试获取几只知名股票数据
            test_codes = ['601611', '002162', '601668', '603993']
            test_quotes = self.quotation.stocks(test_codes)
            
            if test_quotes and len(test_quotes) > 0:
                print(f"✅ 连接测试成功，获取到 {len(test_quotes)} 只股票数据")
                
                # 显示测试数据
                for code, data in list(test_quotes.items())[:2]:  # 只显示前2只
                    name = data.get('name', '未知')
                    price = data.get('now', '0')
                    print(f"   📈 {code} {name} 现价:{price}")
                
                return True
            else:
                print("❌ 连接测试失败，未获取到数据")
                return False
            
        except Exception as e:
            self.logger.error(f"Connection test failed: {e}")
            print(f"❌ 连接测试失败: {e}")
            return False


# 模块级别的便捷函数
def create_ranking_monitor(data_source: str = 'tencent') -> AStockRanking:
    """创建A股涨幅排名监控器实例
    
    Args:
        data_source: 数据源
        
    Returns:
        AStockRanking: 排名监控器实例
    """
    return AStockRanking(data_source=data_source)


if __name__ == "__main__":
    # 直接运行此模块时的测试功能
    print("⚠️  建议使用 'python main.py rank' 来启动涨幅排名功能")
    print("📖 运行 'python main.py --help' 查看所有可用选项")
    
    # 提供基本的直接运行功能
    try:
        ranking = AStockRanking()
        top_gainers = ranking.get_top_gainers(20)  # 获取前20名
        ranking.display_ranking(top_gainers, 20)
    except Exception as e:
        print(f"❌ 启动失败: {e}")
        exit(1) 